﻿using LiteBlog.Common.Entities;
using LiteBlog.Common.Enums;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using LiteBlog.Repositories;
using LiteBlog.Repositories.Common;

namespace LiteBlog.Repositories.Tests
{
    [TestClass]
    public class PostCommentDataTests : RepositoryTests<PostCommentRepository>
    {
        [TestMethod]
        public void CreateWorks()
        {
            var comment = new Comment();
            comment.ID = NewCommentId;
            comment.FileID = FileId;
            comment.Name = UpdatedCommentAuthor;
            comment.Time = Time;
            comment.Text = UpdatedComment;
            comment.Ip = CommentIp;
            _repository.Create(comment);
            _mock.Verify(x => x.Save(It.Is<XElement>((elem) => VerifyCreate(elem)),
                                     It.Is<string>(path => VerifyPath(path, FileId))),
                                     Times.Once);
            
        }

        [TestMethod]
        public void UpdateWorks()
        {
            var comment = new Comment();
            comment.ID = CommentId;
            comment.FileID = FileId;
            comment.Name = UpdatedCommentAuthor;
            comment.Time = Time;
            comment.Text = UpdatedComment;
            _repository.Update(comment);
            _mock.Verify(x => x.Save(It.Is<XElement>((elem) => VerifyUpdate(elem)),
                                     It.Is<string>(path => VerifyPath(path, FileId))),
                                     Times.Once);
        }

        [TestMethod]
        public void DeleteWorks()
        {
            _repository.Delete(CommentId, FileId);
            _mock.Verify(x => x.Save(It.Is<XElement>((elem) => VerifyDelete(elem)),
                                     It.Is<string>(path => VerifyPath(path, FileId))),
                                     Times.Once);
        }

        private bool VerifyCreate(XElement root)
        {
            var commentElem = root.Element("Comments")
                                    .Elements("Comment")
                                    .SingleOrDefault(comment => (string)comment.Attribute("ID") == NewCommentId);

            if (commentElem != null)
            {
                bool result1 = (string)commentElem.Attribute("Name") == UpdatedCommentAuthor;
                bool result2 = commentElem.Value == UpdatedComment;
                bool result3 = (string)commentElem.Attribute("Time") == FormattedTime;
                bool result4 = (string)commentElem.Attribute("Ip") == CommentIp;
                return result1 && result2 && result3 && result4;
            }

            return false;
        }

        private bool VerifyUpdate(XElement root)
        {
            var commentElem =   root.Element("Comments")
                                    .Elements("Comment")
                                    .SingleOrDefault(comment => (string)comment.Attribute("ID") == CommentId);

            if(commentElem!=null)
            {
                bool result1 = (string)commentElem.Attribute("Name") == UpdatedCommentAuthor;
                bool result2 = commentElem.Value == UpdatedComment;
                return result1 && result2;
            }

            return false;
        }

        private bool VerifyDelete(XElement root)
        {
            return !(root.Element("Comments")
                .Elements("Comment")
                .Any(elem => (string)elem.Attribute("ID") == CommentId));
        }

        private bool VerifyPath(string path, string fileId)
        {
            string expectedPath = _repository.DataContext.GetPath(DataStoreType.Post, fileId);
            return expectedPath.Equals(path);
        }

        protected override void SetupLoad()
        {
            var postRoot = new XElement("Post",
                              new XAttribute("FileID", FileId),
                              new XAttribute("Title", Title),
                              new XAttribute("Author", Author),
                              new XAttribute("CatID", "Cat1, Cat2"),
                              new XAttribute("TagID", "Tag1, Tag2"),
                              new XAttribute("Time", FormattedTime),
                              new XAttribute("Comments", 1),
                              new XElement("Content", Content),
                              new XElement("Meta", Meta),
                              new XElement("Comments",
                                  new XElement("Comment",
                                      new XAttribute("ID", CommentId),
                                      new XAttribute("Name", CommentAuthor),
                                      new XAttribute("Url", string.Empty),
                                      new XAttribute("Time", FormattedTime),
                                      new XAttribute("Ip", CommentIp),
                                      new XAttribute("IsAuthor", true),
                                      Comment))
                          );

            string postPath = _repository.DataContext.GetPath(DataStoreType.Post, FileId);
            _mock.Setup(x => x.Load(postPath))
                 .Returns(postRoot);

            string catPath = _repository.DataContext.GetPath(DataStoreType.Category, null);
            var catRoot = new XElement("Categories",
                            new XElement("Category",
                                new XAttribute("ID", "Cat1"),
                                new XAttribute("Name", "Category 1"),
                                new XAttribute("Count", 0),
                                new XAttribute("Order", 1)),
                            new XElement("Category",
                                new XAttribute("ID", "Cat2"),
                                new XAttribute("Name", "Category 2"),
                                new XAttribute("Count", 0),
                                new XAttribute("Order", 2)));

            _mock.Setup(x => x.Load(catPath))
                 .Returns(catRoot);

            string tagPath = _repository.DataContext.GetPath(DataStoreType.Tags, null);
            var tagRoot = new XElement("Tags",
                            new XElement("Tag",
                                new XAttribute("Id", "Tag1"),
                                new XAttribute("Name", "Tag 1"),
                                new XAttribute("Count", 0)),
                            new XElement("Tag",
                                new XAttribute("Id", "Tag2"),
                                new XAttribute("Name", "Tag 2"),
                                new XAttribute("Count", 0)));
            _mock.Setup(x => x.Load(tagPath))
                 .Returns(tagRoot);

            _mock.Setup(x => x.GetInnerXml(It.IsAny<XElement>()))
                 .Returns<XElement>((elem) =>
                 {
                     StringBuilder innerXml = new StringBuilder();
                     elem.Nodes().ToList().ForEach(node => innerXml.Append(node.ToString()));
                     return innerXml.ToString();
                 });
        }

        protected override string GetFileName()
        {
            throw new NotImplementedException();
        }

        [Ignore]
        public override void PathIsCorrect()
        {
        }

        private static readonly string FileId = "TestId";
        private static readonly string Title = "Test";
        private static readonly string Author = "Test Author";
        private static readonly string FormattedTime = "14-Feb-2013 10:21 AM";
        private static readonly DateTime Time = new DateTime(2013, 02, 14, 10, 21, 0);
        private static readonly string Content = "&lt;p&gt;Welcome to my blog!&lt;/p&gt;";
        private static readonly string Meta = "Test post";
        private static readonly string CommentId = "100";
        private static readonly string NewCommentId = "101";
        private static readonly string CommentAuthor = "Comment Author";
        private static readonly string Comment = "Comment";
        private static readonly string UpdatedCommentAuthor = "Comment Author 2";
        private static readonly string UpdatedComment = "Comment 2";
        private static readonly string CommentIp = "127.0.0.1";
    }
}
